﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Socket网络编程Client
{
    public partial class Form1 : Form
    {

        // 下面是我的私有字段
        // 本客户端客户创建的所有通信的Socket
        Dictionary<string, Socket> _socketComDic = new Dictionary<string, Socket>();

        public Form1()
        {
            InitializeComponent();
        }
        
        /// <summary>
        /// 开始连接服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_Connet_Click(object sender, EventArgs e)
        {
            try
            {
                Socket socketCom = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 在这里socketCom.LocalEndPoint与socketCom.RemoteEndPoint均为空

                IPAddress ip = IPAddress.Parse(textBox_IP.Text);
                IPEndPoint port = new IPEndPoint(ip, int.Parse(textBox_Port.Text));
                // 连接一次便会占用一个端口号
                socketCom.Connect(port);

                ShowLog("连接成功");

                _socketComDic.Add(socketCom.LocalEndPoint.ToString(), socketCom);
                comboBox_ClientList.Items.Add(socketCom.LocalEndPoint.ToString());
                comboBox_ClientList.SelectedIndex = comboBox_ClientList.Items.Count - 1;

                Thread reciveThread = new Thread(ReciveTask);
                reciveThread.IsBackground = true;
                reciveThread.Name = "客户端接收服务器端数据线程";
                reciveThread.Start(socketCom);
            }
            catch 
            {
                ShowLog("连接失败");
            }
            
        }
        /// <summary>
        /// 打印日志
        /// </summary>
        /// <param name="str"></param>
        private void ShowLog(string str)
        {
            textBox_Log.AppendText(str+"\r\n");
        }

        /// <summary>
        /// 当前结点接收数据线程，做了多结点多格式区分
        /// </summary>
        /// <param name="arg"></param>
        private void ReciveTask(object arg)
        {           
            //try
            //{
                Socket socketCom = arg as Socket;
                while (true)
                {
                    byte[] buff = new byte[1024*1024*5];


                    // 对socketCom.Receive，我的理解
                    /*
                     * 双方的Socekt在传输我们给他的数据后，应该是有一层属于他的通信协议，
                     * 发送端发送完数据，接收端能根据这份协议判断是否接收完成，注意，这里的接收完成不是针对我们用户的
                     * 数据完整性，而是针对
                     * 1 如果接收完成，程序执行到Receive函数的时候，这个函数就会一直主语监听数据报文的状态，不会跳出
                     * 直到有数据传输过来
                     */

                    /* 1 指定了接收长度int count = socketCom.Receive(buff, 0, buff.Length, SocketFlags.None);
                     * 1.1 如果实际接收的长度小于等于指定长度，就立即返回实际接收长度，并不会等待接收到指定长度才返回
                     * 1.2 如果实际接收的长度大于指定长度，就返回所指定长度的数据，等下一周期再重新接收后面的数据
                     *   2 不指定长度:相当于使用buff的长度指定了接收长度int count = socketCom.Receive(buff);
                     * 2.1 实际接收数据长度小于等于buff的长度，就返回实际接收的数据长度，并不会等待接收满buff才返回
                     * 2.2 实际接收数据长度大于buff的长度，就返回buff长度的数据，然后下一周期再继续接收
                     */

                    int count = socketCom.Receive(buff);
                                      
                    if (count == 0)
                    {
                        break;
                    }

                    if (socketCom.LocalEndPoint.ToString() == comboBox_ClientList.SelectedItem.ToString())
                    {
                        textBox_RX.AppendText(socketCom.LocalEndPoint.ToString() + "收到了\r\n");
                        // 接收到的是文本
                        if (buff[0] == 1)
                        {
                            textBox_RX.AppendText(socketCom.RemoteEndPoint.ToString() + ":" + Encoding.GetEncoding(936).GetString(buff, 1, count-1) + "\r\n");
                        }
                        // 接收到的是文件
                        else if (buff[0] == 2)
                        {
                            SaveFileDialog sfd = new SaveFileDialog();
                            sfd.InitialDirectory = ".";
                            sfd.AddExtension = true;
                            sfd.DefaultExt = ".png";
                            sfd.SupportMultiDottedExtensions = false;
                            sfd.Title = "请选择你要保存的路径";
                            sfd.Filter = "所有文件|*.*";
                            sfd.ShowDialog(this);

                            using(FileStream fw = new FileStream(sfd.FileName,FileMode.OpenOrCreate,FileAccess.Write))
                            {
                                fw.Write(buff,1,count-1);
                            }
                            MessageBox.Show("保存成功");
                        }
                        //震动
                        else if (buff[0] == 3)
                        {    
                            Point point = this.Location;
                            for (int i = 0; i < 500; i++)
                            {
                                point.X += 50;
                                point.Y += 50;
                                this.Location = point;
                                point.X -= 50;
                                point.Y -= 50;
                                this.Location = point;
                            }
                        }
                    }
                }
                //服务器掉线后，释放本地套接字资源,关闭数据接收线程               
                ShowLog("服务器断开了当前套接字的连接");
                socketCom.Close();
                Thread.CurrentThread.Abort();
            
            //}
            //catch(Exception ex)
            {
                //ShowLog(ex.ToString());
            }
            
        }

        /// <summary>
        /// 使用阻塞IO的方式发送字符文本到服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_Send_Click(object sender, EventArgs e)
        {
            byte[] buff = Encoding.GetEncoding(936).GetBytes(textBox_TX.Text);
            _socketComDic[comboBox_ClientList.SelectedItem.ToString()].Send(buff);
        }

        /// <summary>
        /// 加载初始化，取消跨线程调用检查
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 删除已经连接着的结点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_DeletPoint_Click(object sender, EventArgs e)
        {
            MessageBox.Show("确定删除当前选中节点吗？");

            if (comboBox_ClientList.Items.Count != 0)
            {
                _socketComDic[comboBox_ClientList.SelectedItem.ToString()].Disconnect(true);
                _socketComDic.Remove(comboBox_ClientList.SelectedItem.ToString());
                comboBox_ClientList.Items.Remove(comboBox_ClientList.SelectedItem);
            }
            else
            {
                MessageBox.Show("结点已空");
            }
        }
    }
}
